package com.zmn.mcc.permit.mysql.targets;

import com.zmn.common.utils.string.StringUtil;
import com.zmn.mcc.common.constant.DpermitConsts;
import com.zmn.mcc.permit.mysql.annotation.ZmnDPermit;
import com.zmn.mcc.permit.mysql.annotation.ZmnDPermitConfig;
import com.zmn.mcc.permit.mysql.annotation.ZmnDPermitType;
import com.zmn.mcc.permit.mysql.enums.DPermitTypeEnum;
import org.apache.commons.lang.StringUtils;

import java.lang.reflect.InvocationTargetException;
import java.util.*;

/**
 * 反射代理内部工具
 *
 * @author duanzuocai
 * @version v1.0
 * @since 2020/6/23 13:35
 */
public class ZmnPermitDataTargetUtil {

    /**
     *
     * @param annotationStaffId DAO上指定的员工staffId或者loginStaff的值
     * @param daoParameterMap DAO的入参：key-value
     * @return
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public static Object getFieldByMap(String annotationStaffId, Map daoParameterMap) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        if (StringUtils.isEmpty(annotationStaffId)) {
            return null;
        }

        Object filed;

        if (annotationStaffId.indexOf(".") != -1) {
            String[] reference = annotationStaffId.split("\\.");

            String alias = reference[0];
            String attribute = reference[1];

            Object paramObj = daoParameterMap.get(alias);

            filed = paramObj.getClass().getMethod("get" + StringUtil.toUpperCaseForFirstChar(attribute)).invoke(paramObj);
        } else {
            filed = daoParameterMap.get(annotationStaffId);
        }

        return filed;
    }

    /**
     *
     * @param annotationStaffId 员工ID：staffId或者LoginStaff
     * @param daoParameterObj DAO入参：Object
     * @return
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public static Object getFieldByObj(String annotationStaffId, Object daoParameterObj) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        if (StringUtils.isEmpty(annotationStaffId)) {
            return null;
        }

        Object filed;

        if (annotationStaffId.indexOf(".") != -1) {
            String[] reference = annotationStaffId.split("\\.");

            String attribute = reference[1];

            filed = daoParameterObj.getClass().getMethod("get" + StringUtil.toUpperCaseForFirstChar(attribute)).invoke(daoParameterObj);

            if (filed == null) {
                return null;
            }
        } else {
            filed = daoParameterObj;
        }

        return filed;
    }

    /**
     * 取交集：数据权限类型
     *
     * 1、取Dao的type和mapping的注解DPermitDataEnum并集 A
     * 2、取Top的type注解DPermitDataEnum B
     * 3、再取A和B的DPermitDataEnum并集
     *
     * @param daoAnnotation
     * @param topAnnotation
     * @return
     */
    public static Map<Integer, List<DPermitTypeEnum>> getAnnotationTypes(ZmnDPermitConfig daoAnnotation, ZmnDPermit topAnnotation) {
        DPermitTypeEnum[] daoTypes = daoAnnotation.types();
        ZmnDPermitType[] daoMapping = daoAnnotation.mapping();
        DPermitTypeEnum[] topTypes = topAnnotation.types();

        Map<Integer, List<DPermitTypeEnum>> daoTypesAndMappingMap = new HashMap<>(DpermitConsts.TYPE_ALL.size());

        for (ZmnDPermitType mapping : daoMapping) {
            DPermitTypeEnum daoMappingType = mapping.type();
            daoMappingType.setColumn(mapping.column());
            addTypeEnumToMap(daoTypesAndMappingMap, daoMappingType);
        }

        // 添加原生DAO注解
        for (DPermitTypeEnum daoType : daoTypes) {
            addTypeEnumToMap(daoTypesAndMappingMap, daoType);
        }

        // 取交集：注解中数据类型取交集
        Map<Integer, List<DPermitTypeEnum>> sumTypes = new HashMap<>(DpermitConsts.TYPE_ALL.size());
        for (DPermitTypeEnum topType : topTypes) {
            // 数据权限大类型：比如300（区域）301（省）
            int topBigType = topType.getType().getType();

            List<DPermitTypeEnum> daoTypesAndMappings = daoTypesAndMappingMap.get(topBigType);
            if (daoTypesAndMappings == null) {
                continue;
            }

            // 取交集类型对象
            DPermitTypeEnum sumType = searchDPermitTypeEnum(topType.getType().getValue(), daoTypesAndMappings);

            if (sumType == null) {
                continue;
            }

            addToSumTypes(sumTypes, topBigType, sumType);
        }

        // 从小到大排序，验证
        if (sumTypes.size() != 0) {
            for (Integer key : sumTypes.keySet()) {
                Collections.sort(sumTypes.get(key));
            }
        }

        return sumTypes;

    }

    /**
     * 取交集类型对象
     *
     * @param type
     * @param daoTypesAndMappings
     * @return
     */
    private static DPermitTypeEnum searchDPermitTypeEnum(int type, List<DPermitTypeEnum> daoTypesAndMappings) {
        DPermitTypeEnum sumType = null;
        for (DPermitTypeEnum typeEnum : daoTypesAndMappings) {
            if (typeEnum.getType().getValue() == type) {
                sumType = typeEnum;
                break;
            }
        }
        return sumType;
    }

    /**
     * 交集判断成功，把交集对象添加到：sumTypes
     *
     * @param sumTypes
     * @param topTypeValue
     * @param sumType
     */
    private static void addToSumTypes(Map<Integer, List<DPermitTypeEnum>> sumTypes, int topTypeValue, DPermitTypeEnum sumType) {
        List<DPermitTypeEnum> list2 = sumTypes.get(topTypeValue);
        if (list2 == null) {
            list2 = new ArrayList<>();
            sumTypes.put(topTypeValue, list2);
        }
        list2.add(sumType);
    }

    private static void addTypeEnumToMap(Map<Integer, List<DPermitTypeEnum>> topAndChildType, DPermitTypeEnum daoTypeEnum) {
        int topType = daoTypeEnum.getType().getType();

        List<DPermitTypeEnum> childList = topAndChildType.get(topType);

        // 初始化
        if (childList == null) {
            childList = new ArrayList<>();
            topAndChildType.put(topType, childList);
        }


        childList.add(daoTypeEnum);

        Collections.sort(childList);
    }
}
